package com.jiawa.train.business.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiawa.train.business.domain.po.ConfirmOrder;
import com.jiawa.train.business.domain.po.DailyTrainCarriage;
import com.jiawa.train.business.domain.po.DailyTrainSeat;
import com.jiawa.train.business.domain.po.DailyTrainTicket;
import com.jiawa.train.business.dto.ConfirmOrderMQDto;
import com.jiawa.train.business.enums.ConfirmOrderStatusEnum;
import com.jiawa.train.business.enums.RedisKeyPreEnum;
import com.jiawa.train.business.enums.SeatColEnum;
import com.jiawa.train.business.enums.SeatTypeEnum;
import com.jiawa.train.business.feign.MemberFeign;
import com.jiawa.train.business.mapper.ConfirmOrderMapper;
import com.jiawa.train.business.mapper.DailyTrainTicketMapper;
import com.jiawa.train.business.mapper.SkTokenMapper;
import com.jiawa.train.business.req.ConfirmOrderDoReq;
import com.jiawa.train.business.req.ConfirmOrderQueryReq;
import com.jiawa.train.business.req.ConfirmOrderTicketReq;
import com.jiawa.train.business.resp.ConfirmOrderQueryResp;
import com.jiawa.train.business.service.IConfirmOrderService;
import com.jiawa.train.business.service.IDailyTrainCarriageService;
import com.jiawa.train.business.service.IDailyTrainSeatService;
import com.jiawa.train.business.service.IDailyTrainTicketService;
import com.jiawa.train.common.exception.BusinessException;
import com.jiawa.train.common.exception.BusinessExceptionEnum;
import com.jiawa.train.common.req.MemberTicketReq;
import com.jiawa.train.common.resp.CommonResp;
import com.jiawa.train.common.resp.PageResp;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 确认订单 服务实现类
 * </p>
 *
 * @author hrz
 * @since 2025-09-08
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ConfirmOrderServiceImpl extends ServiceImpl<ConfirmOrderMapper, ConfirmOrder> implements IConfirmOrderService {

    private final IDailyTrainTicketService dailyTrainTicketService;
    private final IDailyTrainCarriageService dailyTrainCarriageService;
    private final IDailyTrainSeatService dailyTrainSeatService;
    private final DailyTrainTicketMapper dailyTrainTicketMapper;
    private final MemberFeign memberFeign;
    private final RedissonClient redissonClient;
    private final SkTokenMapper skTokenMapper;
    private final StringRedisTemplate redisTemplate;

    @Override
    public void save(ConfirmOrderDoReq req) {
        ConfirmOrder confirmOrder = BeanUtil.copyProperties(req, ConfirmOrder.class);
        LocalDateTime now = LocalDateTime.now();
        if (ObjectUtil.isNull(confirmOrder.getId())) {
            //增加
            confirmOrder.setCreateTime(now);
            confirmOrder.setUpdateTime(now);
            this.save(confirmOrder);
        } else {
            //更新
            confirmOrder.setUpdateTime(now);
            this.updateById(confirmOrder);
        }
    }

    @Override
    public PageResp<ConfirmOrderQueryResp> queryList(ConfirmOrderQueryReq req) {
        Page<ConfirmOrder> page = this.lambdaQuery()
                .page(new Page<>(req.getPage(), req.getSize()));
        List<ConfirmOrder> records = page.getRecords();
        if (CollUtil.isEmpty(records)) {
            PageResp<ConfirmOrderQueryResp> objectPageResp = new PageResp<>();
            objectPageResp.setList(new ArrayList<>());
            objectPageResp.setTotal(0L);
            return objectPageResp;
        }
        List<ConfirmOrderQueryResp> respList = BeanUtil.copyToList(records, ConfirmOrderQueryResp.class);
        PageResp<ConfirmOrderQueryResp> objectPageResp = new PageResp<>();
        objectPageResp.setList(respList);
        objectPageResp.setTotal(page.getTotal());
        return objectPageResp;
    }

    @Override
    public void delete(Long id) {
        this.removeById(id);
    }

    @Override
    @GlobalTransactional //全局事务
    @SentinelResource(value = "doConfirm", blockHandler = "doConfirmBlock")
    public void doConfirm(ConfirmOrderMQDto req) {
//        //1.校验验证码
//        String imageCodeToken = req.getImageCodeToken();
//        System.out.println(imageCodeToken);
//        String redisCode = redisTemplate.opsForValue().get(imageCodeToken);
//        System.out.println(redisCode);
//        if(ObjectUtil.isNull(redisCode)){
//            log.info("验证码校验失败,redisCode为空");
//            return new CommonResp<>(false,"验证码已过期",null);
//        }
//        if(req.getImageCode().equalsIgnoreCase(redisCode)){
//            log.info("验证码校验成功");
//            //移除缓存中的验证码
//            redisTemplate.delete(imageCodeToken);
//        }else{
//            log.info("验证码校验失败");
//            return new CommonResp<>(false,"验证码错误",null);
//        }
//        //2.校验令牌 - 加乐观锁
//        boolean flag = validateToken(req.getDate()
//                , req.getTrainCode()
//                , LoginMemberContext.getId());
//        if (flag) {
//            log.info("成功获取令牌,可进行购买");
//        } else {
//            log.info("未获取到令牌,无法进行购买");
//            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_SK_TOKEN_FAIL);
//        }
        //3.加锁
        //1.获取分布式锁
        String key = RedisKeyPreEnum.CONFIRM_ORDER.getCode() + req.getTrainCode() + "-" + req.getDate();
        RLock lock = null;
        lock = redissonClient.getLock(key);
        try {
            boolean b = lock.tryLock(0, TimeUnit.SECONDS);
            if (b) {
                log.info("成功获取分布式锁,可进行操作了");
            } else {
                log.info("获取锁失败,稍后重试");
                //throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_EXCEPTION);
                return ;
            }
            //省略业务校验 - 防止恶意访问
            //保存确认订单表
//            ConfirmOrder result = new ConfirmOrder();
//            result.setCreateTime(LocalDateTime.now());
//            result.setUpdateTime(LocalDateTime.now());
//            result.setDate(req.getDate());
//            result.setMemberId(LoginMemberContext.getId());
//            result.setTrainCode(req.getTrainCode());
//            result.setStart(req.getStart());
//            result.setEnd(req.getEnd());
//            result.setDailyTrainTicketId(req.getDailyTrainTicketId());
//            result.setTickets(JSON.toJSONString(req.getTickets()));
//            result.setStatus(ConfirmOrderStatusEnum.INIT.getCode());
            //查出余票记录,得到真实的库存
            //2.循环出票
            while(true){
                //3.去确认订单表中查询所有用户插入的处于当前车次的初始状态的订单
                Page<ConfirmOrder> page = this.lambdaQuery()
                        .eq(ConfirmOrder::getTrainCode, req.getTrainCode())
                        .eq(ConfirmOrder::getDate, req.getDate())
                        .eq(ConfirmOrder::getStatus, ConfirmOrderStatusEnum.INIT.getCode())
                        .orderByAsc(ConfirmOrder::getId)
                        .page(new Page<>(1, 10));
                List<ConfirmOrder> records = page.getRecords();
                if(CollUtil.isEmpty(records)){
                    log.info("本车次订单处理完毕");
                    break;
                }else{
                    log.info("本次处理{}条订单", records.size());
                }
                //循环获取到的订单进行出票 - 将数据库更新到位即可
                for (ConfirmOrder record : records) {
                    try {
                        sell(record);
                    } catch (BusinessException e) {
                        if (e.getE().equals(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR)) {
                            log.info("本订单余票不足，继续售卖下一个订单");
                            record.setStatus(ConfirmOrderStatusEnum.EMPTY.getCode());
                            //将当前订单状态设置为售罄
                            this.lambdaUpdate()
                                    .eq(ConfirmOrder::getId, record.getId())
                                    .set(ConfirmOrder::getStatus, ConfirmOrderStatusEnum.EMPTY.getCode());
                            //注意这里没有直接跳出循环,因为我们不能因为一个车票售罄,就直接跳出循环
                            //因为可能还有其他的车票还未售罄,比如车座类型不同,票的余量就不同
                        } else {
                            throw e;
                        }
                    }
                } //eof for
            } //eof while
        } catch (InterruptedException e) {
            log.error("购票异常", e);
            return ;
        } finally {
            log.info("购票结束,释放锁");
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    @Override
    public Integer queryLineCount(Long id) {
        //根据订单id查询当前订单在队列中的位次返回给前端
        ConfirmOrder one = this.lambdaQuery()
                .eq(ConfirmOrder::getId, id).one();
        int status = switch (EnumUtil.getBy(ConfirmOrderStatusEnum::getCode,one.getStatus())){
            case PENDING -> 0;
            case INIT -> 999;
            case EMPTY -> -3;
            case SUCCESS -> -1;
            case FAILURE -> -2;
            case CANCEL -> -4;
        };
        if(status == 999){
            //说明订单还在初始化,前面有排队的,就需要去订单表中查出前面的订单数
            Long count = this.lambdaQuery()
                    .eq(ConfirmOrder::getStatus, ConfirmOrderStatusEnum.PENDING.getCode())
                    .eq(ConfirmOrder::getTrainCode,one.getTrainCode())
                    .eq(ConfirmOrder::getDate,one.getDate())
                    .or()
                    .eq(ConfirmOrder::getStatus, ConfirmOrderStatusEnum.INIT.getCode())
                    .eq(ConfirmOrder::getTrainCode,one.getTrainCode())
                    .eq(ConfirmOrder::getDate,one.getDate())
                    .lt(ConfirmOrder::getCreateTime, one.getCreateTime())
                    .count();
            return count.intValue();
        }else{
            return status;
        }
    }

    public void selectSeat(DailyTrainTicket t
            , String seatType, /*1 二等座 2 一等座 3 软卧 4 硬卧*/
                           List<Integer> offset,
                           String trainCode,
                           String SeatCol /*A B C...*/
            , List<DailyTrainSeat> finallySeat) {
        //声明一个临时变量用来存储最终选座成功后所有被选择的座位信息
        List<DailyTrainSeat> tempSeat = new ArrayList<>();
        //先根据座位类型获取该车次该座位类型的车厢
        //注意,如果是无选座的话就无需考虑是否连排的问题
        List<DailyTrainCarriage> l = dailyTrainCarriageService
                .selectByTrainCodeAndSeatType(trainCode, seatType);
        for (DailyTrainCarriage c : l) { //遍历车厢
            //进到下一个车厢时就将临时变量清空
            tempSeat = new ArrayList<>();
            //选出该日期的该车次的该车厢的座位信息
            List<DailyTrainSeat> seats = dailyTrainSeatService.lambdaQuery()
                    .eq(DailyTrainSeat::getDate, t.getDate())
                    .eq(DailyTrainSeat::getTrainCode, trainCode)
                    .eq(DailyTrainSeat::getCarriageIndex, c.getIndex())
                    .orderByAsc(DailyTrainSeat::getCarriageSeatIndex)
                    .list();
            for (DailyTrainSeat seat : seats) { //遍历所有座位判断该座位是否可卖
                Integer carriageSeatIndex = seat.getCarriageSeatIndex(); //同车厢的座位序号
                String col = seat.getCol();
                if (finallySeat.contains(seat)) {
                    //说明该座位被选择过了
                    log.info("该座位{}被选择过了,不可重复选中", seat.getCarriageSeatIndex());
                    continue;
                }
                if (StrUtil.isEmpty(SeatCol)) {
                    //说明没有选座
                    log.info("没有选座");
                } else {
                    //说明选座了
                    log.info("选座了");
                    if (!col.equals(SeatCol)) {
                        log.info("选座了,但是座位不匹配 跳过看下一个座位是否符合");
                        continue;
                    }
                }
                boolean b = cellSeat(seat, t.getStartIndex(), t.getEndIndex());
                if (b) {
                    log.info("车厢{}选座成功", c.getIndex());
                    tempSeat.add(seat);
                } else {
                    log.info("选座失败");
                    continue;
                }
                //根据offset选择剩下的座位
                if (CollUtil.isNotEmpty(offset)) {
                    boolean allSeatsBeGet = true;
                    for (int i = 1; i < offset.size(); i++) {
                        //数据库的索引是从1开始的,所以要减一
                        int newIndex = carriageSeatIndex + offset.get(i) - 1; //座位的序号
                        if (newIndex >= seats.size()) {
                            //比如当A1不满足,跳到A2符合了,但是还选了一个C2,
                            //他和A1的偏移量让A2加上如果超过了该车厢的座位数,说明就要去下一个车厢了
                            log.info("该下标已经超过车厢的座位数量");
                            break;
                        }
                        //判断该座位是否可获取 - 此时是索引是从0开始的
                        DailyTrainSeat dailyTrainSeat = seats.get(newIndex);
                        boolean b1 = cellSeat(dailyTrainSeat, t.getStartIndex(), t.getEndIndex());
                        if (b1) {
                            //选座成功
                            tempSeat.add(dailyTrainSeat);
                        } else {
                            //选座失败 - 说明该车厢无法满足,去下一个车厢
                            log.info("选座失败,车厢{}没有该座位的空闲位", c.getIndex());
                            allSeatsBeGet = false; //说明该车厢无法满足,去下一个车厢
                            break;
                        }
                    }
                    if (!allSeatsBeGet) {
                        //说明该车厢或者该车厢这连续的两排已经无法满足,跳出外层循环
                        //去下一排或者下一个车厢
                        //此时,因为座位售卖信息没有持久化,所以不影响上一个座位
                        //清空tempSeat
                        tempSeat = new ArrayList<>();
                        continue;
                    }
                }
                //走到这说明选出了一批符合条件的座位了,可以准备持久化了
                //1.将tempSeat给finallySeat
                finallySeat.addAll(tempSeat);
                return; //如果是无选座就准备找下一个座位,有选座就结束操作了
            }
        }
    }

    public void afterConfirm(
            DailyTrainTicket t
            , String trainCode
            , List<DailyTrainSeat> finallySeat
            , List<ConfirmOrderTicketReq> tickets
            , ConfirmOrder confirmOrder) {
        Integer startIndex = t.getStartIndex();
        Integer endIndex = t.getEndIndex();
        Date date = t.getDate();
        //1.将每日座位的sell情况给更新一下
        for (int i = 0; i < finallySeat.size(); i++) {
            DailyTrainSeat dailyTrainSeat = finallySeat.get(i);
            dailyTrainSeatService.lambdaUpdate()
                    .eq(DailyTrainSeat::getId, dailyTrainSeat.getId())
                    .set(DailyTrainSeat::getSell, dailyTrainSeat.getSell())
                    .set(DailyTrainSeat::getUpdateTime, LocalDateTime.now())
                    .update();
            //2.去更新ticket
            //2.1先计算本车票影响的车次的始发站区间和到达地区间的一些车票的库存
            Integer minEndIndex = startIndex + 1;
            Integer maxStartIndex = endIndex - 1;
            int minStartIndex = 0;
            int maxEndIndex = dailyTrainSeat.getSell().length();
            for (int j = startIndex - 1; j >= 0; j--) {
                if (dailyTrainSeat.getSell().charAt(j) == '1') {
                    minStartIndex = j + 1;
                    break;
                }
            }
            for (int j = endIndex; j <= dailyTrainSeat.getSell().length() - 1; j++) {
                if (dailyTrainSeat.getSell().charAt(j) == '1') {
                    maxEndIndex = j + 1;
                    break;
                }
            }
            log.info("minStartIndex={},maxStartIndex={},minEndIndex={},maxEndIndex={}"
                    , minStartIndex, maxStartIndex, minEndIndex, maxEndIndex);
            log.info("开始更新每日票的库存!!");
            dailyTrainTicketMapper.updateStorage(
                    date
                    , trainCode
                    , dailyTrainSeat.getSeatType()
                    , minStartIndex
                    , maxEndIndex
                    , minEndIndex
                    , maxStartIndex);

            // 调用会员服务接口，为会员增加一张车票
            MemberTicketReq memberTicketReq = new MemberTicketReq();
            memberTicketReq.setMemberId(confirmOrder.getMemberId());
            memberTicketReq.setPassengerId(tickets.get(i).getPassengerId());
            memberTicketReq.setPassengerName(tickets.get(i).getPassengerName());
            memberTicketReq.setTrainDate(date);
            memberTicketReq.setTrainCode(t.getTrainCode());
            memberTicketReq.setCarriageIndex(dailyTrainSeat.getCarriageIndex());
            memberTicketReq.setSeatRow(dailyTrainSeat.getRow());
            memberTicketReq.setSeatCol(dailyTrainSeat.getCol());
            memberTicketReq.setStartStation(t.getStart());
            memberTicketReq.setStartTime(t.getStartTime());
            memberTicketReq.setEndStation(t.getEnd());
            memberTicketReq.setEndTime(t.getEndTime());
            memberTicketReq.setSeatType(dailyTrainSeat.getSeatType());
            /*调用了ITicketService - 也参与到全局事务中*/
            CommonResp<Object> commonResp = memberFeign.save(memberTicketReq);
            log.info("调用member接口，返回：{}", commonResp);

            //修改确认订单状态为成功
            this.lambdaUpdate()
                    .eq(ConfirmOrder::getId, confirmOrder.getId())
                    .set(ConfirmOrder::getStatus, ConfirmOrderStatusEnum.SUCCESS.getCode())
                    .set(ConfirmOrder::getUpdateTime, LocalDateTime.now())
                    .update();
        }
    }

    private boolean cellSeat(DailyTrainSeat seat, int startIndex, int endIndex) {
        //1.获取到座位的售卖信息
        String sell = seat.getSell(); //0000 0110 1001 ...
        log.info("当前座位信息:{},长度:{},startIndex:{},endIndex:{}",
                sell,sell.length(),startIndex,endIndex);
        //2.获取到当前车票的车站区间的售卖信息
        String substring = sell.substring(startIndex, endIndex);
        if (Integer.parseInt(substring) > 0) {
            //说明在车次的该区间该座位已经有人买了,不可再进行购买
            return false;
        } else {
            //说明该区间该座位没有人买,可以进行购买
            //1.先将substring的0给替换为1
            String replace = substring.replace('0', '1');
            log.info("replace - 1:{}",replace);
            //2.将这个新字符串给补上0
            String resultTemp = StrUtil.fillBefore(replace, '0', endIndex);
            log.info("replace - 2:{}",resultTemp);
            String result = StrUtil.fillAfter(resultTemp, '0', sell.length());
            log.info("replace -3 :{}",result);
            //3.将这个字符串与原字符串进行 或 操作
            int newSell = (NumberUtil.binaryToInt(result)) | (NumberUtil.binaryToInt(sell));
            //4.将该数字给转换回二进制
            String binaryStr = NumberUtil.getBinaryStr(newSell);
            log.info("最终座位信息:{}",binaryStr);
            //5.给binaryStr补上0 - 防止原二进制前面开头有0
            String str = StrUtil.fillBefore(result, '0', sell.length());
            System.out.println("-----------------------------"+str);
            //6.给设置回去 - 此时并未持久化
            seat.setSell(str);
            return true;
        }
    }

    private void doConfirmBlock(ConfirmOrderDoReq req, BlockException e) {
        log.info("doConfirmBlock, req={}", req);
        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_FLOW_EXCEPTION);
    }


    private void sell(ConfirmOrder req){
        //进来之后先将订单状态设置为处理中
        this.lambdaUpdate()
                .eq(ConfirmOrder::getId, req.getId())
                .set(ConfirmOrder::getStatus, ConfirmOrderStatusEnum.PENDING.getCode())
                .update();
        //查询该车次当前区间的余票信息
        DailyTrainTicket dailyTrainTicket = dailyTrainTicketService.lambdaQuery()
                .eq(DailyTrainTicket::getDate, req.getDate())
                .eq(DailyTrainTicket::getTrainCode, req.getTrainCode())
                .eq(DailyTrainTicket::getStart, req.getStart())
                .eq(DailyTrainTicket::getEnd, req.getEnd())
                .one();
        log.info("余票信息: {}", dailyTrainTicket);
        List<ConfirmOrderTicketReq> tickets = JSON.parseArray(req.getTickets(), ConfirmOrderTicketReq.class);
        //预扣减余票数量,判断余票是否充足
        for (ConfirmOrderTicketReq ticket : tickets) {
            //1.先获取该预定票的作为类型
            String seatTypeCode = ticket.getSeatTypeCode();
            SeatTypeEnum by = EnumUtil.getBy(SeatTypeEnum::getCode, seatTypeCode);
            switch (by) {
                case YDZ -> {
                    //判断一等座是否充足
                    Integer ydz = dailyTrainTicket.getYdz();
                    if (ydz - 1 < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setEdz(ydz - 1);
                }
                case EDZ -> {
                    //判断二等座是否充足
                    Integer edz = dailyTrainTicket.getEdz();
                    if (edz - 1 < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYdz(edz - 1);
                }
                case RW -> {
                    //判断软卧是否充足
                    Integer rw = dailyTrainTicket.getRw();
                    if (rw - 1 < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setRw(rw - 1);
                }
                case YW -> {
                    //判断硬卧是否充足
                    Integer yw = dailyTrainTicket.getYw();
                    if (yw - 1 < 0) {
                        throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_TICKET_COUNT_ERROR);
                    }
                    dailyTrainTicket.setYw(yw - 1);
                }
            }
        }
        //判断是否选座了 - 从第一个预定票中获取座位 - 如果包含了A1,C2类似信息说明就选座了
        ConfirmOrderTicketReq confirmOrderTicketReq = tickets.get(0);
        String seat = confirmOrderTicketReq.getSeat();
        //4.设置一个最终被选出的座位列表
        List<DailyTrainSeat> finallySeat = new ArrayList<>();
        if (StrUtil.isNotBlank(seat)) {
            //说明进行了选座 - 注意只有全是一等座或二等座且余票充足才可以选座
            //获得座位类型
            String seatTypeCode = confirmOrderTicketReq.getSeatTypeCode();
            //获取所有该类型的座位种类 - A C D F ...
            List<SeatColEnum> colsByType = SeatColEnum.getColsByType(seatTypeCode);
            //创建供计算偏移值的座位列表
            ArrayList<String> seatList = new ArrayList<>();
            for (int i = 0; i < 2; i++) {
                for (SeatColEnum seatColEnum : colsByType) {
                    String seatCode = seatColEnum.getCode() + i;
                    seatList.add(seatCode);
                }
            } //最终得到[A1 C1 D1 F1 A2 C2 D2 F2]
            //计算偏移值
            List<Integer> absoluteOffsetList = new ArrayList<>();
            for (ConfirmOrderTicketReq ticket : tickets) {
                //先计算出绝对偏移值
                String seat1 = ticket.getSeat();
                int i = seatList.indexOf(seat1);
                absoluteOffsetList.add(i);
            }
            //计算出相对第一个选的座的偏移值
            List<Integer> offset = new ArrayList<>();
            for (Integer index : absoluteOffsetList) {
                offset.add(index - absoluteOffsetList.get(0));
            }// [0,2...]
            //选座
            selectSeat(dailyTrainTicket
                    , confirmOrderTicketReq.getSeatTypeCode()
                    , offset
                    , req.getTrainCode()
                    , confirmOrderTicketReq.getSeat().split("")[0]
                    , finallySeat);
        } else {
            //没有选座,随机选座
            for (ConfirmOrderTicketReq ticket : tickets) {
                selectSeat(dailyTrainTicket
                        , ticket.getSeatTypeCode()
                        , null
                        , req.getTrainCode()
                        , null
                        , finallySeat);
            }
        }
        //选座
        //一个车厢一个车厢的获取座位数据
        //挑选符合条件的座位,如果不满足就进入下一个车厢
        log.info("最终选座: {}", finallySeat.stream().map(DailyTrainSeat::getCarriageSeatIndex)
                .collect(Collectors.toList()));
        try {
            //选中座位后事务处理
            //座位表修改sell情况
            //余票详情表修改余票
            //为会员增加购票记录
            //5.更新确认订单为成功
            afterConfirm(dailyTrainTicket, req.getTrainCode(), finallySeat, tickets, req);
        } catch (Exception e) {
            log.error("保存购票信息失败", e);
            throw new BusinessException(BusinessExceptionEnum.CONFIRM_ORDER_EXCEPTION);
        }
    }

//    private boolean validateToken(Date date, String trainCode, Long memberId) {
//        RLock lock = null;
//        String key = date + "-" + trainCode + memberId;
//        lock = redissonClient.getLock(key);
//        boolean trylock = false;
//        try {
//            //注意给锁加上5秒过时时间,防止人反复点击抢票
//            trylock = lock.tryLock(0,10, TimeUnit.SECONDS);
//        } catch (InterruptedException e) {
//            log.info("获取令牌锁出现异常,key={}", key);
//            return false;
//        }
//        if (trylock) {
//            log.info("获取到令牌锁,key={}", key);
//        } else {
//            log.info("未获取到令牌锁,key={}", key);
//            return false;
//        }
//        //获取到令牌锁后去获取令牌
//        String Count_key = RedisKeyPreEnum.SK_TOKEN_COUNT.getCode() + date + "-" + trainCode;
//        //先去缓存尝试获取令牌
//        Object SKTokenCount = redisTemplate.opsForValue().get(Count_key);
//        if(SKTokenCount != null){
//            //说明缓存中存在这个结构
//            Long count = redisTemplate.opsForValue().decrement(Count_key, 1);
//            if(count < 0){
//                //说明令牌已经被抢完了
//                log.info("令牌已经被抢完了,key={}", Count_key);
//                return false;
//            }else{
//                //说明令牌还未被抢完
//                log.info("成功获取令牌,key={}", Count_key);
//                if(count % 10 ==0){
//                    //每10个令牌更新一次到数据库
//                    skTokenMapper.decrease(date, trainCode, 10);
//                }
//                return true;
//            }
//        }else{
//            //缓存中没这个结构,先去数据库查询
//            SkToken skToken = skTokenMapper.selectByTrainAndDate(trainCode, date);
//            if (skToken == null) {
//                //说明数据库中也没有这个结构
//                log.info("数据库中也没有这个结构,key={}", Count_key);
//                return false;
//            }
//            Integer count = skToken.getCount();
//            if(count <= 0){
//                //说明令牌已经被抢完了
//                log.info("令牌已经被抢完了,key={}", Count_key);
//                return false;
//            }else{
//                //说明令牌还未被抢完
//                log.info("成功获取令牌,key={}", Count_key);
//                //放置到缓存 - 更新缓存时间为60秒 - 这里先不更新数据库,由上面的10个令牌更新一次数据库
//                redisTemplate.opsForValue().set(Count_key, (--count).toString(),60, TimeUnit.SECONDS);
//                return true;
//            }
//        }
//    }
}
